namespace SubSonic
{
    using SubSonic.Utilities;
    using System;
    using System.Data;

    [Serializable]
    public abstract class ActiveRecord<T> : AbstractRecord<T> where T: AbstractRecord<T>, new()
    {
        protected ActiveRecord()
        {
            base.MarkNew();
        }

        protected virtual void AfterCommit()
        {
        }

        protected virtual void BeforeInsert()
        {
        }

        protected virtual void BeforeUpdate()
        {
        }

        protected virtual void BeforeValidate()
        {
        }

        public static int Delete(object keyID)
        {
            return ActiveRecord<T>.DeleteByParameter(AbstractRecord<T>.BaseSchema.PrimaryKey.ColumnName, keyID, null);
        }

        public static int Delete(string columnName, object oValue)
        {
            return ActiveRecord<T>.DeleteByParameter(columnName, oValue, null);
        }

        public static int Delete(string columnName, object oValue, string userName)
        {
            return ActiveRecord<T>.DeleteByParameter(columnName, oValue, userName);
        }

        private static int DeleteByParameter(string columnName, object oValue, string userName)
        {
            int num = 0;
            bool flag = AbstractRecord<T>.BaseSchema.Columns.Contains("Deleted");
            bool flag2 = AbstractRecord<T>.BaseSchema.Columns.Contains("IsDeleted");
            bool flag3 = AbstractRecord<T>.BaseSchema.Columns.Contains("ModifiedBy");
            bool flag4 = AbstractRecord<T>.BaseSchema.Columns.Contains("ModifiedOn");
            if (flag || flag2)
            {
                Query query = new Query(AbstractRecord<T>.BaseSchema);
                if (flag)
                {
                    query.AddUpdateSetting("Deleted", true);
                }
                if (flag2)
                {
                    query.AddUpdateSetting("IsDeleted", true);
                }
                if (flag3 && !string.IsNullOrEmpty(userName))
                {
                    query.AddUpdateSetting("ModifiedBy", userName);
                }
                if (flag4)
                {
                    query.AddUpdateSetting("ModifiedOn", DateTime.Now);
                }
                query.AddWhere(columnName, oValue);
                query.Execute();
                return num;
            }
            return ActiveRecord<T>.DestroyByParameter(columnName, oValue);
        }

        public static int Destroy(object keyID)
        {
            return ActiveRecord<T>.DestroyByParameter(AbstractRecord<T>.BaseSchema.PrimaryKey.ColumnName, keyID);
        }

        public static int Destroy(string columnName, object oValue)
        {
            return ActiveRecord<T>.DestroyByParameter(columnName, oValue);
        }

        private static int DestroyByParameter(string columnName, object oValue)
        {
            QueryCommand deleteCommand = ActiveRecord<T>.GetDeleteCommand(columnName, oValue);
            deleteCommand.ProviderName = AbstractRecord<T>.BaseSchema.Provider.Name;
            return DataService.ExecuteQuery(deleteCommand);
        }

        private static T fetchByID(object keyValue)
        {
            if (keyValue != null)
            {
                T local = Activator.CreateInstance<T>();
                Query query = new Query(AbstractRecord<T>.BaseSchema);
                query.AddWhere(AbstractRecord<T>.BaseSchema.PrimaryKey.ColumnName, Comparison.Equals, keyValue);
                IDataReader rdr = DataService.GetReader(query.BuildSelectCommand());
                if (rdr.Read())
                {
                    local.Load(rdr);
                }
                rdr.Close();
                if (!local._isNew && local.IsLoaded)
                {
                    return local;
                }
            }
            return default(T);
        }

        public static T FetchByID(decimal keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static T FetchByID(Guid? keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static T FetchByID(Guid keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static T FetchByID(decimal? keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static T FetchByID(int? keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static T FetchByID(long? keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static T FetchByID(int keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static T FetchByID(long keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static T FetchByID(string keyValue)
        {
            return ActiveRecord<T>.fetchByID(keyValue);
        }

        public static QueryCommand GetDeleteCommand(object keyID)
        {
            Query qry = new Query(AbstractRecord<T>.BaseSchema);
            qry.QueryType = QueryType.Delete;
            qry.AddWhere(AbstractRecord<T>.BaseSchema.PrimaryKey.ColumnName, keyID);
            return DataService.BuildCommand(qry);
        }

        public static QueryCommand GetDeleteCommand(string columnName, object oValue)
        {
            Query qry = new Query(AbstractRecord<T>.BaseSchema);
            qry.QueryType = QueryType.Delete;
            qry.AddWhere(columnName, oValue);
            return DataService.BuildCommand(qry);
        }

        public QueryCommand GetInsertCommand(string userName)
        {
            Query qry = new Query(AbstractRecord<T>.BaseSchema);
            qry.QueryType = QueryType.Insert;
            QueryCommand command = new QueryCommand(DataService.GetSql(qry), base.ProviderName);
            command.ProviderName = AbstractRecord<T>.BaseSchema.Provider.Name;
            foreach (TableSchema.TableColumn column in AbstractRecord<T>.BaseSchema.Columns)
            {
                object now;
                if (column.AutoIncrement || column.IsReadOnly)
                {
                    continue;
                }
                bool flag = true;
                if (Utility.IsMatch(column.ColumnName, "CreatedBy") || Utility.IsMatch(column.ColumnName, "ModifiedBy"))
                {
                    now = userName;
                }
                else if (Utility.IsMatch(column.ColumnName, "CreatedOn") || Utility.IsMatch(column.ColumnName, "ModifiedOn"))
                {
                    now = DateTime.Now;
                }
                else if (column.DataType == DbType.Guid)
                {
                    if (!Utility.IsMatch(column.DefaultSetting, "DEFAULT"))
                    {
                        now = base.GetColumnValue<Guid>(column.ColumnName);
                        bool flag2 = Utility.IsMatch(now.ToString(), Guid.Empty.ToString());
                        if (column.IsNullable && flag2)
                        {
                            now = null;
                        }
                        else if (column.IsPrimaryKey && flag2)
                        {
                            now = Guid.NewGuid();
                        }
                    }
                    else
                    {
                        now = null;
                        flag = false;
                    }
                }
                else
                {
                    now = base.GetColumnValue<object>(column.ColumnName);
                    if ((now != null) && (column.DataType == DbType.Boolean))
                    {
                        if (Utility.IsMatch(now.ToString(), bool.FalseString))
                        {
                            now = 0;
                        }
                        else if (Utility.IsMatch(now.ToString(), bool.TrueString))
                        {
                            now = 1;
                        }
                    }
                }
                if (now == null)
                {
                    now = DBNull.Value;
                }
                if (flag)
                {
                    command.Parameters.Add(ActiveRecord<T>.ParameterPrefix + column.ColumnName, now, column.DataType);
                }
            }
            return command;
        }

        public QueryCommand GetSaveCommand()
        {
            return this.GetSaveCommand(string.Empty);
        }

        public QueryCommand GetSaveCommand(string userName)
        {
            if (base.IsNew)
            {
                return this.GetInsertCommand(userName);
            }
            if (base.IsDirty)
            {
                return this.GetUpdateCommand(userName);
            }
            return null;
        }

        public QueryCommand GetUpdateCommand(string userName)
        {
            Query qry = new Query(AbstractRecord<T>.BaseSchema);
            qry.QueryType = QueryType.Update;
            QueryCommand command = new QueryCommand(DataService.GetSql(qry), base.ProviderName);
            command.ProviderName = AbstractRecord<T>.BaseSchema.Provider.Name;
            foreach (TableSchema.TableColumn column in AbstractRecord<T>.BaseSchema.Columns)
            {
                object now;
                if (column.IsReadOnly)
                {
                    continue;
                }
                if (Utility.IsMatch(column.ColumnName, "ModifiedBy"))
                {
                    now = userName;
                }
                else if (Utility.IsMatch(column.ColumnName, "ModifiedOn"))
                {
                    now = DateTime.Now;
                }
                else if (column.DataType == DbType.Guid)
                {
                    now = base.GetColumnValue<Guid>(column.ColumnName);
                    if (column.IsNullable && Utility.IsMatch(now.ToString(), Guid.Empty.ToString()))
                    {
                        now = null;
                    }
                }
                else
                {
                    now = base.GetColumnValue<object>(column.ColumnName);
                }
                if (now == null)
                {
                    now = DBNull.Value;
                }
                command.Parameters.Add(ActiveRecord<T>.ParameterPrefix + column.ColumnName, now, column.DataType);
            }
            return command;
        }

        [Obsolete("Deprecated: Use AfterCommit() instead.")]
        protected virtual void PostUpdate()
        {
        }

        [Obsolete("Deprecated: Use BeforeInsert() and/or BeforeUpdate() instead.")]
        protected virtual void PreUpdate()
        {
        }

        public void Save()
        {
            this.Save(string.Empty);
        }

        public void Save(Guid userID)
        {
            string userName = userID.ToString();
            this.Save(userName);
        }

        public void Save(int userID)
        {
            this.Save(userID.ToString());
        }

        public void Save(string userName)
        {
            this.BeforeValidate();
            if (this.Validate())
            {
                if (base.IsNew)
                {
                    this.BeforeInsert();
                }
                else if (base.IsDirty)
                {
                    this.BeforeUpdate();
                }
                this.PreUpdate();
                QueryCommand saveCommand = this.GetSaveCommand(userName);
                if (saveCommand != null)
                {
                    object obj2 = DataService.ExecuteScalar(saveCommand);
                    if (obj2 != null)
                    {
                        if (obj2.GetType() == typeof(decimal))
                        {
                            obj2 = Convert.ToInt32(obj2);
                        }
                        if (AbstractRecord<T>.BaseSchema.PrimaryKey.AutoIncrement || (AbstractRecord<T>.BaseSchema.PrimaryKey.DataType == DbType.Guid))
                        {
                            try
                            {
                                base.SetPrimaryKey(obj2);
                            }
                            catch
                            {
                                throw new Exception("No Primary Key is defined for this table. A primary key is required to use SubSonic");
                            }
                        }
                    }
                    base.MarkOld();
                    base.MarkClean();
                    this.AfterCommit();
                    this.PostUpdate();
                }
            }
            else
            {
                string str = string.Empty;
                foreach (string str2 in base.Errors)
                {
                    str = str + str2 + Environment.NewLine;
                }
                throw new Exception("Can't save: " + str);
            }
        }

        public virtual bool Validate()
        {
            this.BeforeValidate();
            base.ValidateColumnSettings();
            return (base.Errors.Count == 0);
        }

        protected static string ParameterPrefix
        {
            get
            {
                return AbstractRecord<T>.BaseSchema.Provider.GetParameterPrefix();
            }
        }
    }
}

